"""A simple agent implementation.

DemoAgent class inherits from BaseAgent and implement all three
abstract methods: setup(), step() and reset().
"""
import copy
import json
import os
import random
import signal
import multiprocessing
import time

from .base_agent import BaseAgent
from .hexmap.astar import gen_path_with_astar
from .map import Map
from .execute import *
from .const import *
from .behavior_trees import *
from .situation import *
from .infer_paras import *
from .mission_schedule import *
from .task_allocation import *
from .whole_mission_schedule import *
from .alliance_allocation import *
from multiprocessing import Queue, RLock

class Agent(BaseAgent):
    def __init__(self):
        self.scenario = None
        self.color = None
        self.priority = None
        self.observation = None
        self.map = None
        self.scenario_info = None
        self.map_data = None
        self.seat = None
        self.faction = None
        self.role = None
        self.controllable_ops = None
        self.team_info = None
        self.my_direction = None
        self.my_mission = None
        self.user_name = None
        self.user_id = None
        self.history = None

        self.my_message = dict()
        self.tmp_mission = None
        self.tmp_action = dict()
        self.first_msg = False
        self.situation = None

        self.city_coord = None
        self.operator_cods = None  # 初始态势的我方算子坐标
        self.operator_center = None  # 初始态势下根据我方算子坐标生成一个中心坐标
        self.enemy_operator_cods = None  # 初始态势的敌方算子坐标
        self.enemy_operator_center = None  # 初始态势下根据敌方算子坐标生成一个中心坐标
        self.city_center = None  # 夺控区域的中心点
        self.city_neighbor_3 = None
        self.city_neighbor_10 = None

        self.minelayer_curve = []  # 布雷车曲线

        self.AntiAircraftGun_defense_center = []  # 防空高炮防守区域的三个中心坐标点
        self.AntiAircraftGun_defense_center_standby = []

        self.AntiAircraftVehi_history_pos = []  # 车载防空导弹车的历史机动目标点位，防止堆叠

        # 扫雷车需要的属性（算子前往夺控点的通路区域)
        self.tanks_pos = None
        self.refer_access_region_pos = []
        self.mine_num = []  # 敌方布雷车的最大布雷数量
        self.access_regions = []
        self.access_regions_standby = []

        self.drone_guide = {}  # 记录无人机引导射击 obj_id : target_obj_id 攻击目标id
        self.drone_infer = {}

        self.judge_jmpoints_historical_positions = {}  # 记录历史推断的见瞄点坐标 obj_id: [step, position]  step: 步数；position: 当时所在格
        self.jmpoints_historical_positions = {}  # 记录推断的见瞄点坐标 obj_id: [step, tar_hex]  # step: 步数；tar_hex：推理的见瞄点
        self.cancel_jmplan = {}  # 记录取消见瞄点信息 用来判断见瞄计划是否取消 obj_id :[step]

        # 步兵乘车请求
        self.request_for_ride = {}
        # 装有 发送请求的
        # {
        # 步兵id : {
        #   防守的夺控点 denfend_city_pos : int
        #   上车位置 boarding_point : int
        #   车辆id viches_id : int
        #   行为类型:action_type : int        这个是为以后预留的，不用管
        #   下车位置 target_pos : int
        #   请求是否被接受 accept_or_not : boolean
        #   是否正在运输 transport_or_not : boolean
        #                     }
        # }
        # accept_or_not为True视为接受请求
        # transport_or_not为True视为正在运输
        # accept_or_not为True transport_or_not为False视为运输完成

        self.IFV_defense_scope = None
        self.IFV_defense_pos = {}
        self.IFV_stop = 0

        self.task = {}  # obj_id : mission_type 记录所有算子下达的任务
        self.tank_defense_scope = None
        self.tank_defense_pos = None

        self.mission_schedule = None
        self.military_strength = None
        self.score = None
        self.target_pos = {}

        self.whole_mission_schedule = None
        self.whole_task_allocation = None

        self.infantrt_defend_city = {}

        # 废弃
        self.squad = None
        # 装有 分队情况
        # {
        # 夺控点id : {
        #   分队任务 mission ： boolen
        #   算子列表 operator_lisy : List
        #   }
        # }

        self.task_allocation = None
        self.last_param = {}

        # 进程相关的属性
        self.situation_process1 = None
        self.situation_process1_start = False
        self.input_queue = None
        self.output_queue = None
        self.input_queue_lock = None
        self.output_queue_lock = None

        # 行为树进程

        self.gen_action_processi = {}  # 存放行为树进程
        self.action_input_queuei = {}  # 行为树进程对应的输入数据队列
        self.action_input_queue_locki = {}  # 行为树进程对应的输入数据的锁
        self.action_output_queuei = {}  # 行为树进程对应的输出数据队列
        self.action_output_queue_locki = {}  # 行为树进程对应的输出数据锁

        # 产生任务的进程的属性
        self.task_process = None
        self.task_input_queue = None
        self.task_output_queue = None
        self.task_input_queue_lock = None
        self.task_output_queue_lock = None

    def setup(self, setup_info):
        """
        增加了距离夺控点距离为10的点 city_neighbor_10 ， 距离夺控点距离为3的点 city_neighbor_3 （bishanning，2022-10-24）
        """
        self.scenario = setup_info["scenario"]
        # self.get_scenario_info(setup_info["scenario"])
        self.color = setup_info["faction"]
        self.faction = setup_info["faction"]
        self.seat = setup_info["seat"]
        self.role = setup_info["role"]
        self.user_name = setup_info["user_name"]
        self.user_id = setup_info["user_id"]
        self.priority = {
            ActionType.Occupy: self.gen_occupy,
            ActionType.Shoot: self.gen_shoot,
            ActionType.GuideShoot: self.gen_guide_shoot,
            ActionType.JMPlan: self.gen_jm_plan,
            ActionType.LayMine: self.gen_lay_mine,
            ActionType.ActivateRadar: self.gen_activate_radar,
            ActionType.ChangeAltitude: self.gen_change_altitude,
            ActionType.GetOn: self.gen_get_on,
            ActionType.GetOff: self.gen_get_off,
            # ActionType.Fork: self.gen_fork,
            ActionType.Union: self.gen_union,
            ActionType.EnterFort: self.gen_enter_fort,
            ActionType.ExitFort: self.gen_exit_fort,
            ActionType.Move: self.gen_move,
            ActionType.RemoveKeep: self.gen_remove_keep,
            ActionType.ChangeState: self.gen_change_state,
            # ActionType.StopMove: self.gen_stop_move,
            ActionType.WeaponLock: self.gen_WeaponLock,
            ActionType.WeaponUnFold: self.gen_WeaponUnFold,
            # ActionType.CancelJMPlan: self.gen_cancel_JM_plan
        }  # choose action by priority
        self.observation = None
        self.map = Map(
            setup_info["basic_data"],
            setup_info["cost_data"],
            setup_info["see_data"]
        )  # use 'Map' class as a tool
        self.map_data = self.map.get_map_data()
        self.bts = {}  # 各算子的执行代码（行为树）
        self.city_coord = [city["coord"] for city in self.scenario["cities"]]
        self.operator_cods = [operator["cur_hex"] for operator in filter(lambda operator:
                              operator['color'] == self.color, self.scenario['operators'])]
        self.enemy_operator_cods = [operator["cur_hex"] for operator in filter(lambda operator:
                                    operator['color'] != self.color, self.scenario['operators'])]
        row_list = []
        col_list = []
        for i in self.operator_cods:
            row_operator, col_operator = divmod(i, 100)
            row_list.append(row_operator)  # 行
            col_list.append(col_operator)  # 列
        row_min = min(row_list)
        col_min = min(col_list)
        row_max = max(row_list)
        col_max = max(col_list)
        self.operator_center = (row_min + int(0.5 * (row_max - row_min))) * 100 + \
                               (col_min + int(0.5 * (col_max - col_min)))
        row_list = []
        col_list = []
        for i in self.enemy_operator_cods:
            row_operator, col_operator = divmod(i, 100)
            row_list.append(row_operator)  # 行
            col_list.append(col_operator)  # 列
        row_min = min(row_list)
        col_min = min(col_list)
        row_max = max(row_list)
        col_max = max(col_list)
        self.enemy_operator_center = (row_min + int(0.5 * (row_max - row_min))) * 100 + \
                                     (col_min + int(0.5 * (col_max - col_min)))
        self.situation = Situation()
        self.situation.setup(self)
        row_list = []
        col_list = []
        for i in self.city_coord:
            row_city, col_city = divmod(i, 100)
            row_list.append(row_city)  # 行
            col_list.append(col_city)  # 列
        row_min = min(row_list)
        col_min = min(col_list)
        row_max = max(row_list)
        col_max = max(col_list)
        self.city_center = (row_min + int(0.5 * (row_max - row_min))) * 100 + \
                           (col_min + int(0.5 * (col_max - col_min)))
        self.city_neighbor_3 = self.get_distance_coord(3)  # 距离夺控点距离为3的所有坐标
        self.city_neighbor_10 = self.get_distance_coord(10)  # 距离夺控点距离为3的所有坐标
        enemy_direction = get_direction(self.city_center, self.enemy_operator_center)
        if enemy_direction == 5:
            self.minelayer_curve = [get_arc(self.city_center, 5, enemy_direction), get_arc(self.city_center, 5, 0),
                                    get_arc(self.city_center, 5, 4), get_arc(self.city_center, 5, 3)]
            self.situation.main_battlefield_refer_work_center = \
                [get_hex_in_direction(self.situation.refer_city_changes_center, 5, 5),
                 get_hex_in_direction(self.situation.refer_city_changes_center, 0, 5),
                 get_hex_in_direction(self.situation.refer_city_changes_center, 4, 5)]
        else:
            all_directions = [0, 1, 2, 3, 4, 5]
            direction_index = all_directions.index(enemy_direction)
            self.minelayer_curve = [get_arc(self.city_center, 5, enemy_direction), get_arc(self.city_center, 5,
                                    all_directions[direction_index+1]),
                                    get_arc(self.city_center, 5, all_directions[direction_index-1]),
                                    get_arc(self.city_center, 5, all_directions[direction_index-2])]
            self.situation.main_battlefield_refer_work_center = \
                [get_hex_in_direction(self.situation.refer_city_changes_center, enemy_direction, 5),
                 get_hex_in_direction(self.situation.refer_city_changes_center, all_directions[direction_index + 1], 5),
                 get_hex_in_direction(self.situation.refer_city_changes_center, all_directions[direction_index - 1], 5)]
        self.situation.main_battlefield_refer_work_center_standby = \
            copy.deepcopy(self.situation.main_battlefield_refer_work_center)
        self.situation.patrol_standby = self.situation.main_battlefield_refer_work_center + \
                                        self.situation.refer_city_changes
        # 防空高炮防守的三个区域的中心坐标
        my_direction = get_direction(self.city_center, self.operator_center)
        if my_direction == 5:
            self.AntiAircraftGun_defense_center = [get_hex_in_direction(self.city_center, 0, 12),
                                                   get_hex_in_direction(self.city_center, 4, 12),
                                                   get_hex_in_direction(self.city_center, 5, 12)]
        else:
            all_directions = [0, 1, 2, 3, 4, 5]
            direction_index = all_directions.index(my_direction)
            self.AntiAircraftGun_defense_center = [get_hex_in_direction
                                                   (self.city_center, all_directions[direction_index-1], 12),
                                                   get_hex_in_direction
                                                   (self.city_center, all_directions[direction_index+1], 12),
                                                   get_hex_in_direction(self.city_center, my_direction, 12)]
        self.AntiAircraftGun_defense_center_standby = copy.deepcopy(self.AntiAircraftGun_defense_center)
        # 扫雷车需要的属性
        self.tanks_pos = [operator["cur_hex"] for operator in filter(lambda operator:
                          operator['color'] == self.color and operator["sub_type"] == 0, self.scenario['operators'])]
        self.mine_num = [operator["remaining_mine_count"] for operator in filter(lambda operator:
                         operator['color'] != self.color and operator["sub_type"] == 13, self.scenario['operators'])]
        for i in self.tanks_pos:
            if not self.refer_access_region_pos:  # 只会执行一次，添加第一个参考点
                self.refer_access_region_pos.append(i)
            for j in self.refer_access_region_pos:
                if self.map.get_distance(i, j) < 5:
                    break  # 已经在记录点的半径5范围内的点不继续记录
                if j == self.refer_access_region_pos[-1]:
                    self.refer_access_region_pos.append(i)
        for i in self.refer_access_region_pos:
            point_set = set()
            route, _ = self.map.gen_move_route(i, self.city_center, 0)
            if len(route) > 8:
                route0 = route[5:-3]
            else:
                route0 = route
            for j in route0:
                point_set0 = self.map.get_grid_distance(j, 0, 4)
                point_set = point_set.union(point_set0)
            self.access_regions.append(list(point_set))
        self.access_regions_standby = copy.deepcopy(self.access_regions)
        for operator in self.scenario['operators']:
            if operator['color'] == self.color:
                self.last_param.update({operator['obj_id']: None})
                self.target_pos.update({operator['obj_id']: None})
            if operator['color'] == self.color and operator['sub_type'] == 5:
                self.drone_guide.update({operator['obj_id']: None})
                self.drone_infer.update({operator['obj_id']: [0, None]})
                self.drone_guide.update({operator['obj_id']: [None]})
                self.judge_jmpoints_historical_positions.update({operator['obj_id']: [0, None]})
                self.jmpoints_historical_positions.update({operator['obj_id']: [0, None]})
                self.cancel_jmplan.update({operator['obj_id']: [None]})  # 初始化
            if operator['color'] == self.color and operator['sub_type'] == BopSubType.IFV:
                self.IFV_defense_pos.update({operator['obj_id']: None})
        self.mission_schedule = MissionSchedule(self)
        # self.mission.bind('attack',self.state_machine)
        self.whole_mission_schedule = WholeMissionSchedule(self)
        self.task_allocation = TaskAllocation()
        self.whole_task_allocation = ALLIANCEAllocation()
        self.whole_task_allocation.setup(self)
        self.task_allocation.setup(self)
        # 进程相关的属性
        if self.role == 0:
            self.situation_process1 = multiprocessing.Process(target=self.situation.update, args=(self,))
            self.output_queue = Queue(1)
            self.input_queue = Queue(2)
            self.output_queue_lock = RLock()
            self.input_queue_lock = RLock()

            # 行为树进程
            tanks_process, Infantry_process, UGV_process, single_process = self.gen_process_group()
            self.tanks_process, self.Infantry_process, self.UGV_process, self.single_process \
                = tanks_process, Infantry_process, UGV_process, single_process
            for i in tanks_process:
                self.gen_action_processi[i] = multiprocessing.Process(target=tank_gen_action, args=(self, i))
                self.action_input_queuei[i] = Queue(3)
                self.action_input_queue_locki[i] = RLock()
                self.action_output_queuei[i] = Queue(1)
                self.action_output_queue_locki[i] = RLock()
            for i in Infantry_process:
                self.gen_action_processi[i] = multiprocessing.Process(target=Infantry_gen_action, args=(self, i))
                self.action_input_queuei[i] = Queue(3)
                self.action_input_queue_locki[i] = RLock()
                self.action_output_queuei[i] = Queue(1)
                self.action_output_queue_locki[i] = RLock()
            for i in UGV_process:
                self.gen_action_processi[i] = multiprocessing.Process(target=UGV_gen_action, args=(self, i))
                self.action_input_queuei[i] = Queue(3)
                self.action_input_queue_locki[i] = RLock()
                self.action_output_queuei[i] = Queue(1)
                self.action_output_queue_locki[i] = RLock()
            # task进程
            self.task_process = multiprocessing.Process(target=gen_task_process, args=(self,))
            self.task_output_queue = Queue(1)
            self.task_input_queue = Queue(2)
            self.task_output_queue_lock = RLock()
            self.task_input_queue_lock = RLock()


    # def command(self, observation):
    #     self.team_info = observation["role_and_grouping_info"]
    #     return (
    #         self.gen_grouping_info(observation)
    #         + self.gen_battle_direction_info(observation)
    #         + self.gen_battle_mission_info(observation)
    #     )

    # def deploy(self, observation):
    #     self.team_info = observation["role_and_grouping_info"]
    #     self.controllable_ops = observation["role_and_grouping_info"][self.seat][
    #         "operators"
    #     ]
    #     communications = observation["communication"]
    #     for command in communications:
    #         if command["info"]["company_id"] == self.seat:
    #             if command["type"] == 200:
    #                 self.my_mission = command
    #             elif command["type"] == 201:
    #                 self.my_direction = command
    #     actions = []
    #     for item in observation["operators"]:
    #         if item["obj_id"] in self.controllable_ops:
    #             operator = item
    #             if operator["sub_type"] == 2 or operator["sub_type"] == 4:
    #                 actions.append(
    #                     {
    #                         "actor": self.seat,
    #                         "obj_id": operator["obj_id"],
    #                         "type": 303,
    #                         "target_obj_id": operator["launcher"],
    #                     }
    #                 )
    #     return actions

    def reset(self):
        # 清空子进程
        if self.role == 0:
            pid = self.situation_process1.pid
            print("hhhhhhh", pid)
            self.input_queue_lock.acquire()
            self.output_queue_lock.acquire()  # 等待队列中的数据传入完毕，再结束进程防止子进程异常中断
            os.kill(pid, signal.SIGTERM)  # 子进程异常终端的时候可能会报错
            self.output_queue_lock.release()
            self.input_queue_lock.release()
            for i in self.gen_action_processi:
                pidi = self.gen_action_processi[i].pid
                self.action_input_queue_locki[i].acquire()
                self.action_output_queue_locki[i].acquire()
                os.kill(pidi, signal.SIGTERM)
                self.action_output_queue_locki[i].release()
                self.action_input_queue_locki[i].release()
            pid_task = self.task_process.pid
            self.task_input_queue_lock.acquire()
            self.task_output_queue_lock.acquire()
            os.kill(pid_task, signal.SIGTERM)
            self.task_output_queue_lock.release()
            self.task_input_queue_lock.release()
        self.scenario = None
        self.color = None
        self.priority = None
        self.observation = None
        self.map = None
        self.scenario_info = None
        self.map_data = None
        self.my_message = dict()
        self.tmp_mission = None
        self.tmp_action = dict()
        self.first_msg = False

    def step(self, observation: dict):
        begin = time.time()
        self.observation = observation  # save observation for later use
        self.situation.update_synchronous(self)  # 同步更新situation中的重要属性
        self.team_info = observation["role_and_grouping_info"]
        self.controllable_ops = observation["role_and_grouping_info"][self.seat]["operators"]
        mine_info = self.team_info.get(self.seat)

        communications = observation["communication"]
        if len(communications) > 0:
            for command in communications:
                if command.__contains__("info"):
                    if command["info"]["company_id"] == self.seat:
                        if command["type"] == 200:
                            self.my_mission = command
                        elif command["type"] == 201:
                            self.my_direction = command
                else:
                    if command["actor"] == self.seat:
                        if command["type"] == 204 or command["type"] == 205 or command["type"] == 206:
                            if self.my_message.__contains__(command["type"]):
                                self.my_message[command["type"]].append(command)
                            else:
                                self.my_message[command["type"]] = []
        else:
            self.my_mission = None

        total_actions = []
        print(f'{self.color} actions at step: {observation["time"]["cur_step"]}')
        if observation["time"]["stage"] == 1:
            actions = []
            for item in observation["operators"]:
                if item["obj_id"] in self.controllable_ops:
                    operator = item
                    if operator["sub_type"] == 2 or operator["sub_type"] == 4:
                        actions.append(
                            {
                                "actor": self.seat,
                                "obj_id": operator["obj_id"],
                                "type": 303,
                                "target_obj_id": operator["launcher"],
                            }
                        )
            #  输出333结束部署动作
            actions.append({
                "actor": self.seat,
                "type": 333
            })
            return actions

        # TODO: 调试用
        # if mine_info["role"] == 0:
        #     print()

        # 算子类型：3-炮兵, 4-无人战车, 5-无人机, 7-巡飞弹, 9-侦察型战车
        # 任务类型：0-攻击，1-防御，2-侦察
        if not self.controllable_ops:
            return []
        if mine_info['role'] == 1:
            if observation["time"]["cur_step"] == 0:
                group_type = [BopSubType.UGV, BopSubType.IFV, BopSubType.CruiseMissle, BopSubType.Infantry,
                              BopSubType.Drone, BopSubType.Tank, BopSubType.Artillery]  # TODO: [3, 4, 5]
                total_actions.append(
                    self.gen_grouping_info(self.observation, group_type))  # 将指定类型算子编组给AI，gen_grouping_info函数有问题
                # 分队决策结束以后在进行行为决策
                return total_actions

            if observation["time"]["cur_step"] == 1:  # TODO
                total_actions.append(self.gen_battle_mission_info(MissionType.Attack))


            print("Human.controllable_ops: ", self.controllable_ops)
        else:
            print("AI.controllable_ops: ", self.controllable_ops)


        ### TODO：群队级代码
        # 在正常推进阶段再开始
        if observation["time"]["stage"] == 2:
            # 进程更改测试

            if not self.situation_process1_start:
                self.situation_process1.start()
                self.task_process.start()
                for i in self.gen_action_processi:
                    self.gen_action_processi[i].start()
                self.situation_process1_start = True

            # agent通过输入队列传入态势observation与经过同步更新的situation
            # if self.observation["time"]["cur_step"] < 2875:  # 在2875之后可能锁在situation中没有释放就被中断了
            self.input_queue_lock.acquire()  # 防止agent与situation同时操作队列造成堵塞
            if self.input_queue.full():
                self.input_queue.get()
                self.input_queue.get()
            self.input_queue.put(self.situation)
            self.input_queue.put(observation)
            self.input_queue_lock.release()

            self.output_queue_lock.acquire()
            if self.output_queue.full():
                situ = self.output_queue.get()
                situ.id = self.situation.id
                situ.enemy_id = self.situation.enemy_id
                situ.enemy_coord = self.situation.enemy_coord
                situ.enemy = self.situation.enemy
                situ.enemy_all_info = self.situation.enemy_all_info
                self.situation = situ
            self.output_queue_lock.release()

            # 生成task的进程与当前agent进行信息传递
            self.task_input_queue_lock.acquire()
            if self.task_input_queue.full():
                self.task_input_queue.get()
                self.task_input_queue.get()
            self.task_input_queue.put(self.situation.enemy)
            self.task_input_queue.put(observation)
            self.task_input_queue_lock.release()

            self.task_output_queue_lock.acquire()
            if self.task_output_queue.full():
                self.task = self.task_output_queue.get()
                print("假假按揭", self.task)
            self.task_output_queue_lock.release()


            # ## 意图层：事件触发的判断 产生新的mission
            # self.whole_mission_schedule.update(self)
            # # print('意图层结束')
            # # 如果态势发生改变
            # if self.whole_mission_schedule.mission_flag:
            #     print('-----------------------------------')
            #     print('态势发生变化')
            #     # ## 兵力筹划层：对算子进行分队和意图推理
            #     # self.squad = self.whole_task_allocation.step(self,self.whole_task_allocation.mission)
            #     # ## 任务分配层：对每个分队进行任务分配 最后放到一个list中交给算子行为层执行
            #     # for i in self.squad:
            #     #     self.task += self.task_allocation.step(self, self.squad)
            #     ## 兵力筹划层+任务分配层：对算子进行分队和意图推理和分配任务
            #     tasks = self.whole_task_allocation.step(self, self.whole_task_allocation.mission)
            #     for i in tasks:
            #         for j in i['member']:
            #             operator_task_dict = {j[0]:[j[1],i['city']]}
            #             self.task.update(operator_task_dict)
            #     print('任务分配结束')
            #
            #     print(type(self.task))

            # agent通过输入队列传入行为决策需要的属性【当前态势，历史态势，任务分配的结果】
            a = time.time()
            # print(a-begin)
            # ceshi
            for i in self.gen_action_processi:
                # 不同类型的算子放入的数据不同
                if i in self.tanks_process:
                    self.action_input_queue_locki[i].acquire()
                    if self.action_input_queuei[i].full():
                        self.action_input_queuei[i].get()
                        self.action_input_queuei[i].get()
                        self.action_input_queuei[i].get()
                    self.action_input_queuei[i].put(observation)
                    self.action_input_queuei[i].put(self.situation.enemy)
                    self.action_input_queuei[i].put(self.task)
                    self.action_input_queue_locki[i].release()
                if i in self.Infantry_process:
                    self.action_input_queue_locki[i].acquire()
                    if self.action_input_queuei[i].full():
                        self.action_input_queuei[i].get()
                        self.action_input_queuei[i].get()
                        self.action_input_queuei[i].get()
                    self.action_input_queuei[i].put(observation)
                    self.action_input_queuei[i].put(1)
                    self.action_input_queuei[i].put(self.task)
                    self.action_input_queue_locki[i].release()
                if i in self.UGV_process:
                    self.action_input_queue_locki[i].acquire()
                    if self.action_input_queuei[i].full():
                        self.action_input_queuei[i].get()
                        self.action_input_queuei[i].get()
                        self.action_input_queuei[i].get()
                    self.action_input_queuei[i].put(observation)
                    self.action_input_queuei[i].put(1)
                    self.action_input_queuei[i].put(self.task)
                    self.action_input_queue_locki[i].release()

        # ### TODO：群队级代码结束
        #
        # ### TODO：分队级代码
        # # 在正常推进阶段再开始
        # if observation["time"]["stage"] == 2:
        #     # TODO：事件触发的判断 产生新的mission
        #     self.mission_schedule.update(self)
        #
        #     # TODO: 任务分配
        #     if self.mission_schedule.mission_flag:
        #         self.task = self.task_allocation.step(self, self.mission_schedule.mission)
        #         print(f"===任务分配: {self.task}")
        # ### TODO：分队级代码结束

        #     #根据self.mission.mission作为分配任务的根据
        #     self.mission_schedule.mission_flag = False
        #     pass
        begin1 = time.time()
        # print("其余时间消耗:",begin1 - begin)
        for obj_id, valid_actions in observation["valid_actions"].items():
            if (self.controllable_ops and obj_id not in self.controllable_ops) or not self.priority:
                continue
            # if not self.tmp_action.__contains__(obj_id):
            #     self.tmp_action[obj_id] = False
            bop = self.get_bop(obj_id)
            if bop['sub_type'] == BopSubType.CruiseMissle:
                if obj_id not in self.bts.keys():
                    self.bts[obj_id] = CruiseMissilesTree()
                self.bts[obj_id].update_bt(self, obj_id)
                if self.bts[obj_id].actions:
                    print(f"===巡飞弹行动: {self.bts[obj_id].actions}")
                total_actions += self.bts[obj_id].actions
                continue
            # 如果能夺控，直接夺控 (yuxh, 2022-10-24)
            if ActionType.Occupy in valid_actions.keys():
                total_actions.append({
                    "actor": self.seat,
                    "obj_id": obj_id,
                    "type": ActionType.Occupy
                })
        # 取出主进程产生的动作
        total_actions += self.gen_action()
        if time.time()-begin1 > 0.1:
            print("主进程产生动作的时间消耗", time.time()-begin1)
        # 通过行为进程的队列取出决策的动作
        process_action = []
        begin2 = time.time()
        for i in self.gen_action_processi:
            if self.action_output_queuei[i].empty():  # 没有输出的时候不用获取释放锁
                continue
            self.action_output_queue_locki[i].acquire()
            if self.action_output_queuei[i].full():
                process_action += self.action_output_queuei[i].get()
            self.action_output_queue_locki[i].release()
        total_actions += process_action
        if time.time()-begin1 > 0.1:
            print("主进程产生动作的时间消耗99", time.time()-begin1, time.time()-begin2)
            print("ceshi", total_actions)
        # if total_actions:
        #     print(f'{self.color} actions at step: {observation["time"]["cur_step"]}', end='\n\t')
        #     print(total_actions)
        # 控制每一个step的时间下限（在online的时候需要更改）
        # end = time.time()
        # step_time = end - begin
        # if step_time > 0.1:
        #     print("一个step花费的时间为：", step_time)
        # else:
        #     time.sleep(0.1-step_time)
        return total_actions

    def get_scenario_info(self, scenario: int):
        SCENARIO_INFO_PATH = os.path.join(
            os.path.dirname(__file__), f"scenario_{scenario}.json"
        )
        with open(SCENARIO_INFO_PATH, encoding="utf8") as f:
            self.scenario_info = json.load(f)

    def gen_process_group(self):
        operators = [operator for operator in filter(lambda operator:
                     operator['color'] == self.color, self.scenario['operators'])]
        tanks_ids = [operator["obj_id"] for operator in operators if operator["sub_type"] == 0]
        Infantry_bops = [operator for operator in operators if operator["sub_type"] == 2]
        UGV_ids = [operator["obj_id"] for operator in operators if operator["sub_type"] == 4]
        single_process = [operator["obj_id"] for operator in operators if operator["sub_type"] not in [0, 1, 2, 4]]
        tanks_process = []
        cur_num = []
        for i in tanks_ids:  # 坦克每2个一组，剩余1个不够数量为2一组的时候将剩余的一个添加到已有的队伍中
            cur_num.append(i)
            if len(cur_num) == 2:
                tanks_process.append(cur_num)
                cur_num = []
            if i == tanks_ids[-1] and cur_num:
                random_group = random.choice(tanks_process)
                random_group.append(i)
                cur_num = []
        Infantry_process = []  # 将步兵与其所属战车分为一组
        for i in Infantry_bops:
            cur_num.append(i["obj_id"])
            cur_num.append(i["car"])
            if len(cur_num) == 2:
                Infantry_process.append(cur_num)
                cur_num = []
        UGV_process = []
        for i in UGV_ids:
            cur_num.append(i)
            if len(cur_num) == 3:
                UGV_process.append(cur_num)
                cur_num = []
            if len(cur_num) == 2 and i == UGV_ids[-1]:
                UGV_process.append(cur_num)
                cur_num = []
            if len(cur_num) == 1 and i == UGV_ids[-1]:
                UGV_process.append(cur_num)
                cur_num = []
        tanks_process_tuple = []
        for i in tanks_process:
            tanks_process_tuple.append(tuple(i))
        Infantry_process_tuple = []
        for i in Infantry_process:
            Infantry_process_tuple.append(tuple(i))
        UGV_process_tuple = []
        for i in UGV_process:
            UGV_process_tuple.append(tuple(i))
        return tanks_process_tuple, Infantry_process_tuple, UGV_process_tuple, single_process


    def gen_action(self):
        total_actions = []
        for obj_id, valid_actions in self.observation["valid_actions"].items():
            bop = self.get_bop(obj_id)
            if (self.controllable_ops and obj_id not in self.controllable_ops) or not self.priority:
                continue
            if bop['sub_type'] == BopSubType.Drone:
                if obj_id not in self.bts.keys():
                    self.bts[obj_id] = drone_tree()
                self.bts[obj_id].update_bt(self, obj_id)
                if self.bts[obj_id].actions:
                    print(f"===无人机行动: {self.bts[obj_id].actions}")
                total_actions += self.bts[obj_id].actions
                continue
        return total_actions

    def get_bop(self, obj_id):
        """Get bop in my observation based on its id."""
        for bop in self.observation["operators"] + self.observation["passengers"]:
            if obj_id == bop["obj_id"]:
                return bop

    def gen_occupy(self, obj_id, candidate):
        """Generate occupy action."""
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": ActionType.Occupy,
        }


    def pos_is_see(self, pos):  # 一个六角格是否被我方所观察通视 w
        for operator in self.observation["operators"]:
            if operator["color"] != self.color:
                continue
            if operator["type"] == 3:
                if self.map.get_distance(operator['cur_hex'], pos) < 3 \
                        and self.map.can_see(operator['cur_hex'], pos, 0):
                    return True
            else:
                if self.map.get_distance(operator['cur_hex'], pos) < 26 \
                        and self.map.can_see(operator['cur_hex'], pos, 0):
                    return True
        return False

    def get_distance_coord(self, a):
        """
        获得距离夺控点距离为a的所有坐标 毕珊宁 2022-10-25
        :param self
        :param a  :a为设定的距离
        :return: distance_coord
        """
        distance_coord = []
        cityneighbor_new = set()  # 定义为一个空set
        city_coord = self.city_coord  # 拿出夺控点
        if len(city_coord) > 1:  # 如果夺控点个数>1 此时需要取并集
            for destination in city_coord:  # 遍历夺控点
                cityneighbor = self.map.get_grid_distance(destination, 0, a)  # 取出以夺控点为圆心距离为a的坐标群 类型为set
                cityneighbor_new = cityneighbor_new.union(cityneighbor)  # 两个集合取并集
                # city_neighbor = cityneighbor  # 更新city_neighbor为下一次取并集做准备
            distance_coord = list(cityneighbor_new)  # 把set类型转化成list
        else:  # 如果只有一个夺控点
            cityneighbor = self.map.get_grid_distance(city_coord[0], 0, a)  # 直接取出以夺控点为圆心距离为a的坐标群
            distance_coord = list(cityneighbor)  # 转换成list
        return distance_coord

    def gen_shoot(self, obj_id, candidate):  # 射击
        # TODO：考虑shoot和guide_shoot的合并
        """
        Generate shoot action with the highest attack level.
        修改了无人战车射击方式，先引导射击，再射击（bisanning, 2022-10-24）
        增加了巡飞弹的射击方式，射击顺序：步兵、重型战车、残血坦克、无人战车（毕珊宁，2022-10-29）
        增加了步兵的射击方式，先引导射击，再射击（袁铎，2023-1-3）
        """
        bop = self.get_bop(obj_id)
        tanks_guide = []  # 坦克
        tanks_shoot = []
        vehicles_guide = []  # 战车
        vehicles_shoot = []  # 重型战车
        ugvs_guide = []  # 无人战车（引导射击）
        ugvs_shoot = []  # 无人战车（射击）
        drone_shoot = []
        soldiers = []  # 步兵
        others_shoot = []
        others_guide = []
        if bop['sub_type'] == BopSubType.Infantry:  # 如果是步兵
            if ActionType.GuideShoot in self.observation["valid_actions"][
                obj_id].keys():  # 如果引导射击在有效动作中 提出来 有引导射击先引导 然后在射击
                candidates1 = self.observation["valid_actions"][obj_id][ActionType.GuideShoot]
                for candidate in candidates1:
                    bop_target = self.get_bop(candidate['target_obj_id'])  # getbop目标id 然后获得他们类型
                    if bop_target["sub_type"] == BopSubType.IFV:  # 如果目标算子类型为战车 找出攻击等级最大的
                        vehicles_guide.append(candidate)
                    elif bop_target["sub_type"] == BopSubType.Tank:  # 如果目标算子类型为坦克
                        tanks_guide.append(candidate)
                    elif bop_target["sub_type"] == BopSubType.UGV:  # 如果目标算子类型为无人战车
                        ugvs_guide.append(candidate)
                    else:
                        others_guide.append(candidate)
                if vehicles_guide:
                    best = max(vehicles_guide, key=lambda x: x["attack_level"])
                elif tanks_guide:
                    best = max(tanks_guide, key=lambda x: x["attack_level"])
                elif ugvs_guide:
                    best = max(ugvs_guide, key=lambda x: x["attack_level"])
                else:
                    best = max(others_guide, key=lambda x: x["attack_level"])
                if others_guide:
                    for i in others_guide:
                        bop_ = self.get_bop(i['target_obj_id'])
                        if bop_["sub_type"] == BopSubType.Infantry:
                            if bop_['keep']:
                                best = i
                return {
                    "actor": self.seat,
                    "obj_id": obj_id,
                    "type": ActionType.GuideShoot,
                    "target_obj_id": best["target_obj_id"],
                    "weapon_id": best["weapon_id"],
                    "guided_obj_id": best["guided_obj_id"],  # 引导射击对象
                }
                # 如果射击在有效动作中 步兵 重型战车 无人战车 坦克 炮兵 无人机
            if ActionType.Shoot in self.observation["valid_actions"][
                obj_id].keys():  # 如果射击在有效动作中 提出来 有引导射击先引导 然后在射击
                candidates2 = self.observation["valid_actions"][obj_id][ActionType.Shoot]
                for candidate0 in candidates2:
                    bop_target0 = self.get_bop(candidate0['target_obj_id'])  # getbop目标id 然后获得他们类型
                    if bop_target0["sub_type"] == BopSubType.Infantry:  # 步兵
                        soldiers.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.IFV:  # 重型战车
                        vehicles_shoot.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.Drone:  # 无人机
                        drone_shoot.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.UGV:  # 无人战车
                        ugvs_shoot.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.Tank:  # 坦克
                        tanks_shoot.append(candidate0)
                    else:  # 其他
                        others_shoot.append(candidate0)
                if soldiers:
                    best = max(soldiers, key=lambda x: x["attack_level"])
                elif vehicles_shoot:
                    best = max(vehicles_shoot, key=lambda x: x["attack_level"])
                elif drone_shoot:
                    best = max(drone_shoot, key=lambda x: x["attack_level"])
                elif ugvs_shoot:
                    best = max(ugvs_shoot, key=lambda x: x["attack_level"])
                elif tanks_shoot:
                    best = max(tanks_shoot, key=lambda x: x["attack_level"])
                else:
                    best = max(others_shoot, key=lambda x: x["attack_level"])
                if others_guide:
                    for i in others_guide:
                        bop_ = self.get_bop(i['target_obj_id'])
                        if bop_["sub_type"] == BopSubType.Infantry:
                            if bop_['keep']:
                                best = i
                return {
                    "actor": self.seat,  # 席位
                    "obj_id": obj_id,  # 算子id
                    "type": ActionType.Shoot,
                    "target_obj_id": best["target_obj_id"],  # 射击算子id
                    "weapon_id": best["weapon_id"],  # 武器id
                }
        if bop['sub_type'] == BopSubType.UGV:  # W我方为无人战车
            if ActionType.GuideShoot in self.observation["valid_actions"][
                obj_id].keys():  # 如果引导射击在有效动作中 提出来 有引导射击先引导 然后在射击
                candidates1 = self.observation["valid_actions"][obj_id][ActionType.GuideShoot]
                for candidate in candidates1:
                    bop_target = self.get_bop(candidate['target_obj_id'])  # getbop目标id 然后获得他们类型
                    if bop_target["sub_type"] == BopSubType.IFV:  # 如果目标算子类型为战车 找出攻击等级最大的
                        vehicles_guide.append(candidate)
                    elif bop_target["sub_type"] == BopSubType.Tank:  # 如果目标算子类型为坦克
                        tanks_guide.append(candidate)
                    elif bop_target["sub_type"] == BopSubType.UGV:  # 如果目标算子类型为无人战车
                        ugvs_guide.append(candidate)
                    else:
                        others_guide.append(candidate)
                if vehicles_guide:
                    best = max(vehicles_guide, key=lambda x: x["attack_level"])
                elif tanks_guide:
                    best = max(tanks_guide, key=lambda x: x["attack_level"])
                elif ugvs_guide:
                    best = max(ugvs_guide, key=lambda x: x["attack_level"])
                else:
                    best = max(others_guide, key=lambda x: x["attack_level"])
                return {
                    "actor": self.seat,
                    "obj_id": obj_id,
                    "type": ActionType.GuideShoot,
                    "target_obj_id": best["target_obj_id"],
                    "weapon_id": best["weapon_id"],
                    "guided_obj_id": best["guided_obj_id"],  # 引导射击对象
                }
            # 如果射击在有效动作中 步兵 重型战车 无人战车 坦克 炮兵
            if ActionType.Shoot in self.observation["valid_actions"][obj_id].keys():  # 如果引导射击在有效动作中 提出来 有引导射击先引导 然后在射击
                candidates2 = self.observation["valid_actions"][obj_id][ActionType.Shoot]
                for candidate0 in candidates2:
                    bop_target0 = self.get_bop(candidate0['target_obj_id'])  # getbop目标id 然后获得他们类型
                    if bop_target0["sub_type"] == BopSubType.Infantry:  # 步兵
                        soldiers.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.IFV:  # 重型战车
                        vehicles_shoot.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.UGV:  # 无人战车
                        ugvs_shoot.append(candidate0)
                    elif bop_target0["sub_type"] == BopSubType.Tank:  # 坦克
                        tanks_shoot.append(candidate0)
                    else:  # 其他
                        others_shoot.append(candidate0)
                if soldiers:
                    best = max(soldiers, key=lambda x: x["attack_level"])
                elif vehicles_shoot:
                    best = max(vehicles_shoot, key=lambda x: x["attack_level"])
                elif ugvs_shoot:
                    best = max(ugvs_shoot, key=lambda x: x["attack_level"])
                elif tanks_shoot:
                    best = max(tanks_shoot, key=lambda x: x["attack_level"])
                else:
                    best = max(others_shoot, key=lambda x: x["attack_level"])
                if others_guide:
                    for i in others_guide:
                        bop_ = self.get_bop(i['target_obj_id'])
                        if bop_["sub_type"] == BopSubType.Infantry:
                            if bop_['keep']:
                                best = i
                return {
                    "actor": self.seat,  # 席位
                    "obj_id": obj_id,  # 算子id
                    "type": ActionType.Shoot,
                    "target_obj_id": best["target_obj_id"],  # 射击算子id
                    "weapon_id": best["weapon_id"],  # 武器id
                }
        if bop['sub_type'] == BopSubType.CruiseMissle:  # 如果是巡飞弹
            if ActionType.Shoot in self.observation["valid_actions"][obj_id].keys():  # 如果射击在有效动作中
                candidates3 = self.observation["valid_actions"][obj_id][ActionType.Shoot]
                # 射击顺序：步兵、残血坦克、重型战车、无人战车
                for candidate3 in candidates3:
                    bop_target1 = self.get_bop(candidate3['target_obj_id'])
                    if bop_target1["sub_type"] == BopSubType.Infantry:  # 步兵
                        soldiers.append(candidate3)
                    elif bop_target1["sub_type"] == BopSubType.Tank:  # 坦克
                        if bop_target1['blood'] < bop_target1['max_blood']:  # 残血坦克
                            tanks_shoot.append(candidate3)
                        else:  # 不是残血的时候的添加
                            others_shoot.append(candidate3)
                    elif bop_target1["sub_type"] == BopSubType.IFV:  # 重型战车
                        vehicles_shoot.append(candidate3)
                    elif bop_target1["sub_type"] == BopSubType.UGV:  # 无人战车
                        ugvs_shoot.append(candidate3)
                    else:  # 其他
                        others_shoot.append(candidate3)
                if soldiers:  # 先步兵
                    best = max(soldiers, key=lambda x: x["attack_level"])
                elif tanks_shoot:  # 残血坦克
                    best = max(tanks_shoot, key=lambda x: x["attack_level"])
                elif vehicles_shoot:  # 重型战车
                    best = max(vehicles_shoot, key=lambda x: x["attack_level"])
                elif ugvs_shoot:  # 无人战车
                    best = max(ugvs_shoot, key=lambda x: x["attack_level"])
                else:  # 其他
                    #TODO：这出现了参数为空的情况
                    best = max(others_shoot, key=lambda x: x["attack_level"])
                if others_guide:
                    for i in others_guide:
                        bop_ = self.get_bop(i['target_obj_id'])
                        if bop_["sub_type"] == BopSubType.Infantry:
                            if bop_['keep']:
                                best = i
                return {
                    "actor": self.seat,  # 席位
                    "obj_id": obj_id,  # 算子id
                    "type": ActionType.Shoot,
                    "target_obj_id": best["target_obj_id"],  # 射击算子id
                    "weapon_id": best["weapon_id"],  # 武器id
                }
        if bop['sub_type'] == BopSubType.Drone:  # 无人机
            if ActionType.GuideShoot in self.observation["valid_actions"][obj_id].keys():  # 如果引导射击在有效动作中
                candidates5 = self.observation["valid_actions"][obj_id][ActionType.GuideShoot]
                for candidate5 in candidates5:
                    bop_target2 = self.get_bop(candidate5['target_obj_id'])  # getbop目标id 然后获得他们类型
                    if bop_target2["sub_type"] == BopSubType.IFV:  # 如果目标算子类型为战车 找出攻击等级最大的
                        vehicles_guide.append(candidate5)
                    elif bop_target2["sub_type"] == BopSubType.UGV:  # 如果目标算子类型为无人战车
                        ugvs_guide.append(candidate5)
                    else:
                        others_guide.append(candidate5)
                if vehicles_guide:
                    best = max(vehicles_guide, key=lambda x: x["attack_level"])
                elif ugvs_guide:
                    best = max(ugvs_guide, key=lambda x: x["attack_level"])
                else:
                    best = max(others_guide, key=lambda x: x["attack_level"])
                if obj_id in self.drone_guide.keys():  # 只更新攻击算子id
                    if best['target_obj_id'] != self.drone_guide[obj_id][best['target_obj_id']]:
                        self.drone_guide[obj_id] = best["target_obj_id"]
                else:
                    self.drone_guide.update({obj_id: best["target_obj_id"]})
                if obj_id in self.drone_infer.keys():
                    self.drone_infer[obj_id] = [self.observation['time'], 0]
                return {
                    "actor": self.seat,
                    "obj_id": obj_id,
                    "type": ActionType.GuideShoot,
                    "target_obj_id": best["target_obj_id"],
                    "weapon_id": best["weapon_id"],
                    "guided_obj_id": best["guided_obj_id"],  # 引导射击对象
                }
        best = max(candidate, key=lambda x: x["attack_level"])
        return {
            "actor": self.seat,  # 席位
            "obj_id": obj_id,  # 算子id
            "type": ActionType.Shoot,
            "target_obj_id": best["target_obj_id"],  # 射击算子id
            "weapon_id": best["weapon_id"],  # 武器id
        }

    def gen_guide_shoot(self, obj_id, candidate):
        """Generate guide shoot action with the highest attack level."""
        best = max(candidate, key=lambda x: x["attack_level"])
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": ActionType.GuideShoot,
            "target_obj_id": best["target_obj_id"],
            "weapon_id": best["weapon_id"],
            "guided_obj_id": best["guided_obj_id"],
        }

    def gen_jm_plan(self, obj_id, candidate, target_pos=None):
        """Generate jm plan action aimed at a random city."""
        weapon_id = random.choice(candidate)["weapon_id"]
        if target_pos:
            jm_pos = random.choice(target_pos)
        else:
            jm_pos = random.choice([city["coord"] for city in self.observation["cities"]])
        return {
                   "actor": self.seat,
                   "obj_id": obj_id,
                   "type": ActionType.JMPlan,
                   "jm_pos": jm_pos,
                   "weapon_id": weapon_id,
               }, jm_pos

    def gen_get_on(self, obj_id, candidate):
        """Generate get on action with some probability."""
        get_on_prob = 0.5
        if random.random() < get_on_prob:
            target_obj_id = random.choice(candidate)["target_obj_id"]
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.GetOn,
                "target_obj_id": target_obj_id,
            }

    def gen_get_off(self, obj_id, candidate):
        """Generate get off action only if the bop is within some distance of a random city."""
        bop = self.get_bop(obj_id)
        destination = random.choice(
            [city["coord"] for city in self.observation["cities"]]
        )
        if bop and self.map.get_distance(bop["cur_hex"], destination) <= 10:
            target_obj_id = random.choice(candidate)["target_obj_id"]
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.GetOff,
                "target_obj_id": target_obj_id,
            }

    def gen_change_state(self, obj_id, candidate):
        """Generate change state action with some probability."""
        change_state_prob = 0.001
        if random.random() < change_state_prob:
            target_state = random.choice(candidate)["target_state"]
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.ChangeState,
                "target_state": target_state,
            }

    def gen_remove_keep(self, obj_id, candidate):
        """Generate remove keep action with some probability."""
        remove_keep_prob = 0.2
        if random.random() < remove_keep_prob:
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.RemoveKeep,
            }

    #
    # def gen_move(self, obj_id, candidate):
    #     """Generate move action to a random city."""
    #
    #     operators = self.observation['operators']
    #     enemy_cur_hex = [bop['cur_hex'] for bop in filter(lambda bop: bop['color'] != self.color, operators)]
    #
    #     bop = self.get_bop(obj_id)
    #     if bop["sub_type"] == 3:
    #         return
    #     if candidate:
    #         destination = random.choice(candidate)
    #     else:
    #         destination = random.choice([city["coord"] for city in self.observation["cities"]])
    #
    #     if self.my_direction:
    #         destination = self.my_direction["info"]["target_pos"]
    #     if bop and bop["cur_hex"] != destination:
    #         move_type = self.get_move_type(bop)
    #         func_idx = random.choice((0, 1))
    #         # print(func_idx)
    #         func_dict = {
    #             0: self.map.gen_move_route_ops,
    #             1: self.map.gen_move_route
    #         }
    #
    #         using_func = func_dict[func_idx]
    #         route, attention_pos = using_func(bop["cur_hex"], destination, move_type, enemy_cur_hex)
    #         return {
    #             "actor": self.seat,
    #             "obj_id": obj_id,
    #             "type": ActionType.Move,
    #             "move_path": route,
    #         }, destination, attention_pos

    def gen_move(self, obj_id, candidate):
        """
        Generate move action to a random city.
        在基准AI的基础上，增加了无人战车的机动，10格外3步一走，10格内一步一走 (bishanning, 2022-10-24)
        增加了巡飞弹的机动，5格一走（毕珊宁，2022-10-29）
        增加了无人机的机动，避开步兵的位置，3格一走,（毕珊宁，2022-10-30）
        增加了坦克的机动，3格一走（毕珊宁，2022-12-23）
        增加了步兵的机动（袁铎，2023-1-3）
        增加了战车的机动，直接走到目标位置（2022-1-5）
        """
        bop = self.get_bop(obj_id)
        if bop["sub_type"] == 3:
            return None, None, None
        if candidate:
            try:
                assert isinstance(candidate, int) or isinstance(candidate, list)
                if isinstance(candidate, int):
                    destination = candidate
                else:
                    destination = random.choice(candidate)
            except Exception as e:
                print("参数candidate类型错误!")
            # 如果算子当前位置在某个未夺取且可以占领的夺控点一格以内，就把目标位置暂时改成夺控点
            for city in self.observation["cities"]:
                tag = True
                if bop['cur_hex'] in self.map.get_neighbors(city['coord']):
                    if city['flag'] != self.color:
                        city_nei = self.map.get_neighbors(city['coord'])
                        city_nei.append(city['coord'])
                        for i in self.observation['operators']:
                            if i['color'] != self.color:
                                if i['cur_hex'] in city_nei:
                                    tag = False
                                    break
                        if tag:
                            destination = city["coord"]
                            break
        else:
            destination = random.choice([city["coord"] for city in self.observation["cities"]])

        if obj_id in self.target_pos.keys():
            if self.target_pos[obj_id] != destination:
                self.target_pos[obj_id] = destination
            else:
                self.target_pos.update({obj_id: destination})

        if bop["sub_type"] == BopSubType.UGV:  # 如果是无人战车
            if bop["cur_hex"] not in self.city_neighbor_10:  # 当前点不在10格以内
                move_type = self.get_move_type(bop)
                route0, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)  # 规划路径

                route = route0[0: 3]
                return {
                    "actor": self.seat,
                    "obj_id": obj_id,
                    "type": ActionType.Move,
                    "move_path": route,
                }  # TODO 返回三个值
            # if bop["cur_hex"] in self.city_neighbor_10:  # 出发点到距离夺控点时采用单格移动的方式
            else:
                move_type = self.get_move_type(bop)
                route0, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)
                if route0:
                    route = [route0[0]]
                    return {
                        "actor": self.seat,
                        "obj_id": obj_id,
                        "type": ActionType.Move,
                        "move_path": route,
                    }  # TODO 返回三个值
        if bop['sub_type'] == BopSubType.CruiseMissle:  # 巡飞弹
            move_type = self.get_move_type(bop)
            route0, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)  # 规划路径
            if len(route0) > 5:
                route = route0[0: 5]
            else:
                route = route0
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.Move,
                "move_path": route,
            }  # TODO 返回三个值
        if bop['sub_type'] == BopSubType.Infantry:  # 步兵 在距离大于2时正常机动,距离等于2时如果不疲劳则二级冲锋,一级疲劳则一级冲锋,距离等于1时相同
            if self.map.get_distance(bop["cur_hex"], destination) > 2:
                target_state = 0
            elif self.map.get_distance(bop["cur_hex"], destination) == 2:
                if bop['tire'] == 0:
                    target_state = 3
                elif bop['tire'] == 1:
                    target_state = 2
                elif bop['tire'] == 2:
                    target_state = 65535
            else:
                if bop['tire'] == 0:
                    target_state = 3
                elif bop['tire'] == 1:
                    target_state = 2
                elif bop['tire'] == 2:
                    target_state = 65535
            move_type = self.get_move_type(bop)
            route0, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)
            # if route0:
            #     if isinstance(route0, list):
            #         route = [route0[0]]
            # else:
            #     route = destination
            if target_state == 65535:
                return None, None, None
            if target_state != bop['move_state']:
                return {
                           "actor": self.seat,
                           "obj_id": obj_id,
                           "type": ActionType.ChangeState,
                           "target_state": target_state,
                       }, None, None
            else:
                return {
                           "actor": self.seat,
                           "obj_id": obj_id,
                           "type": ActionType.Move,
                           "move_path": route0,
                       }, None, None
        if bop['sub_type'] == BopSubType.Drone:  # 无人机
            no_pass_list = []
            # 取出敌人坐标
            if self.situation.enemy:  # 判断能否看到敌人
                for enemy in self.situation.enemy.items():
                    enemy_id = enemy[1]['obj_id']
                    enemy_bop = self.get_bop(enemy_id)
                    if enemy_bop:
                        if enemy_bop['sub_type'] == BopSubType.Infantry:  # 如果敌人是步兵
                            no_pass_list.append(enemy_bop['cur_hex'])  # 取出步兵坐标
            _, route0 = gen_path_with_astar(bop["cur_hex"], destination, 3, self.map, no_pass_list, maxsteps=200)
            if len(route0) > 3:
                route = route0[0: 3]
            else:
                route = route0
            return {
                       "actor": self.seat,
                       "obj_id": obj_id,
                       "type": ActionType.Move,
                       "move_path": route,
                   } \
                , None, None  # TODO 返回三个值
        if bop['sub_type'] == BopSubType.Tank:  # 坦克
            move_type = self.get_move_type(bop)
            route0, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)  # 规划路径
            if len(route0) > 5:
                route = route0[0: 5]
            else:
                route = route0
            return {
                       "actor": self.seat,
                       "obj_id": obj_id,
                       "type": ActionType.Move,
                       "move_path": route,
                   } \
                , None, None  # TODO 返回三个值
        if bop['sub_type'] == BopSubType.IFV:  # 重型战车
            move_type = self.get_move_type(bop)
            route, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)  # 规划路径
            return {
                       "actor": self.seat,
                       "obj_id": obj_id,
                       "type": ActionType.Move,
                       "move_path": route,
                   } \
                , None, None  # TODO 返回三个值
        # 原来的机动方式
        operators = self.observation['operators']
        enemy_cur_hex = [bop['cur_hex'] for bop in filter(lambda bop: bop['color'] != self.color, operators)]
        if self.my_direction:
            destination = self.my_direction["info"]["target_pos"]
        if bop and bop["cur_hex"] != destination:
            move_type = self.get_move_type(bop)
            func_idx = random.choice((0, 1))
            # print(func_idx)
            func_dict = {
                0: self.map.gen_move_route_ops,
                1: self.map.gen_move_route
            }
            using_func = func_dict[func_idx]
            route, attention_pos = using_func(bop["cur_hex"], destination, move_type, enemy_cur_hex)
            return {
                       "actor": self.seat,
                       "obj_id": obj_id,
                       "type": ActionType.Move,
                       "move_path": route,
                   }, destination, attention_pos

    def get_move_type(self, bop):
        """Get appropriate move type for a bop."""
        bop_type = bop["type"]
        if bop_type == BopType.Vehicle:
            if bop["move_state"] == MoveType.March:
                move_type = MoveType.March
            else:
                move_type = MoveType.Maneuver
        elif bop_type == BopType.Infantry:
            move_type = MoveType.Walk
        else:
            move_type = MoveType.Fly
        return move_type

    def gen_stop_move(self, obj_id, candidate):
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": ActionType.StopMove,
        }

    def gen_WeaponLock(self, obj_id, candidate):
        bop = self.get_bop(obj_id)
        prob_weaponlock = 0.001
        if (
                max(self.map_data[bop["cur_hex"] // 100][bop["cur_hex"] % 100]["roads"]) > 0
                or random.random() < prob_weaponlock
        ):
            return {"actor": self.seat, "obj_id": obj_id, "type": ActionType.WeaponLock}

    def gen_WeaponUnFold(self, obj_id, candidate):
        bop = self.get_bop(obj_id)
        destination = random.choice(
            [city["coord"] for city in self.observation["cities"]]
        )
        if self.map.get_distance(bop["cur_hex"], destination) <= 10:
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.WeaponUnFold,
            }

    def gen_cancel_JM_plan(self, obj_id, candidate):
        # cancel_prob = 0.0001
        # if random.random() < cancel_prob:
        """
        毕珊宁 2022-11-16
        增加self.cancel_jmplan 来记录取消见瞄射击的算子
        """
        if obj_id in self.cancel_jmplan.keys():  # 如果被记录过 改变步长
            self.cancel_jmplan[obj_id][0] = self.observation['time']['cur_step']
        else:  # 否则更新步长和算子id
            self.cancel_jmplan.update({obj_id: [self.observation['time']['cur_step']]})
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": ActionType.CancelJMPlan,
        }

    def gen_grouping_info(self, observation, group_type):
        # 增加了步兵和战车 袁铎 2023-1-3
        human_list = []
        ai_list = []
        valid_type = [0, 1, 2, 3, 4, 5, 7, 9]
        for operator in observation["operators"] + observation["passengers"]:
            if operator["color"] == self.color:
                if operator['sub_type'] in valid_type and operator['sub_type'] in group_type:
                    ai_list.append(operator["obj_id"])
                else:
                    human_list.append(operator["obj_id"])

        lists_of_ops = [[], []]
        lists_of_ops[0] = human_list
        lists_of_ops[1] = ai_list

        grouping_info = {"actor": self.seat, "type": 100}
        info = {}
        index = 0
        for teammate_id in self.team_info.keys():
            info[teammate_id] = {"operators": lists_of_ops[index]}
            index += 1
            if index > 1:  # 多余一个AI时不会分配算子，当设计队长AI时，此处要修改。
                break
        grouping_info["info"] = info
        return grouping_info

    def gen_battle_direction_info(self, observation):
        direction_info = []
        for teammate_id in self.team_info.keys():
            direction = {
                "actor": self.seat,
                "type": 201,
                "info": {
                    "company_id": teammate_id,
                    "target_pos": random.choice(observation["cities"])["coord"],
                    "start_time": 0,
                    "end_time": 1800,
                },
            }
            direction_info.append(direction)
        return direction_info

    def gen_battle_mission_info(self, mission_type, target_pos=None, route=None, start_time=0, end_time=1800):
        mission = {
            "actor": self.seat,
            "type": 200,
            "info": {
                "company_id": list(self.team_info.keys())[1],
                "mission_type": mission_type,
                "target_pos": target_pos,
                "route": route,
                "start_time": start_time,
                "end_time": end_time,
            },
        }
        return mission

    def gen_cancel_battle_mission(self, msg_id):
        mission = {
            "actor": self.seat,
            "type": 202,
            "msg_id": msg_id
        }
        return mission

    def gen_fork(self, obj_id, candidate):
        prob = 0.01
        if random.random() < prob:
            return None
        return {"actor": self.seat, "obj_id": obj_id, "type": ActionType.Fork}

    def gen_union(self, obj_id, candidate):
        prob = 0.1
        if random.random() < prob:
            return None
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "target_obj_id": random.choice(candidate)["target_obj_id"],
            "type": ActionType.Union,
        }

    def gen_change_altitude(self, obj_id, candidate):
        prob = 0.05
        if random.random() < prob:
            return None
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "target_obj_id": random.choice(candidate)["target_altitude"],
            "type": ActionType.ChangeAltitude,
        }

    def gen_activate_radar(self, obj_id, candidate):
        prob = 1
        if random.random() < prob:
            return None
        return {"actor": self.seat, "obj_id": obj_id, "type": ActionType.ActivateRadar}

    def gen_enter_fort(self, obj_id, candidate):
        prob = 0.5
        if random.random() < prob:
            return None
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": ActionType.EnterFort,
            "target_obj_id": random.choice(candidate)["target_obj_id"],
        }

    def gen_exit_fort(self, obj_id, candidate):
        prob = 0.1
        if random.random() < prob:
            return None
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": ActionType.ExitFort,
            "target_obj_id": random.choice(candidate)["target_obj_id"],
        }

    def gen_lay_mine(self, obj_id, candidate):
        prob = 1
        if random.random() < prob:
            return None
        return {
            "actor": self.seat,
            "obj_id": obj_id,
            "type": 20,
            "target_pos": random.randint(0, 9177),
        }

    def gen_move_decided(self, obj_id, target_pos=None, route=None):
        bop = self.get_bop(obj_id)
        if bop["sub_type"] == 3 or (not target_pos and not route):
            return
        if target_pos:
            destination = target_pos
        else:
            destination = route[-1]
        my_route = []
        if bop and bop["cur_hex"] != destination:
            move_type = self.get_move_type(bop)
            if not route:
                my_route, _ = self.map.gen_move_route(bop["cur_hex"], destination, move_type)
            else:
                start_pos = bop["cur_hex"]
                route.append(destination)
                for pos in route:
                    if self.map.gen_move_route(start_pos, pos, move_type):
                        tmp, _ = self.map.gen_move_route(start_pos, pos, move_type)
                        for p in tmp:
                            my_route.append(p)
                        start_pos = pos
            return {
                "actor": self.seat,
                "obj_id": obj_id,
                "type": ActionType.Move,
                "move_path": my_route,
            }

    def get_see_scope(self, bop, target_type=0):
        """
        根据bop对target_type类型的观测距离，计算是否可见相应位置的该类算子
        注： 此处考虑了遮蔽地形，通视距离减半
            由于新引擎的observe_distance格式由dict类型变为list类型，因此target_type需要输入sub_type
        """
        try:
            bop_dis = bop['observe_distance'][target_type]  # 获得bop对target_bop的通视距离

            """
            【bop：target_type】，无人机（巡飞弹）：无人机：1，无人机：直升机：5，其他2，flag = True
            直升机：无人机：5，flag = False，直升机：直升机：4，flag = True，其他3，flag = True
            其他：无人机：2，flag = False，其他：直升机：3，flag = False，其他：其他：0，flag = True
            """
            if bop["sub_type"] in [5, 7] and target_type == 5:
                mode = 1
                flag = True
            elif bop["sub_type"] in [5, 7] and target_type == 6:
                mode = 5
                flag = True
            elif bop["sub_type"] in [5, 7]:
                mode = 5
                flag = True
            elif bop["sub_type"] in [6, 8] and target_type == 5:
                mode = 5
                flag = False
            elif bop["sub_type"] in [6, 8] and target_type == 6:
                mode = 4
                flag = True
            elif bop["sub_type"] in [6, 8]:
                mode = 3
                flag = True
            elif target_type == 5:
                mode = 2
                flag = False
            elif target_type == 6:
                mode = 3
                flag = False
            else:
                mode = 0
                flag = True
            if flag:
                # 获得正方形区域
                row, col = bop['cur_hex'] // 100, bop['cur_hex'] % 100
                row0, row1 = min(row - bop_dis, 0), min(row + bop_dis + 1, self.map.max_row)
                col0, col1 = min(col - bop_dis, 0), min(col + bop_dis + 1, self.map.max_col)

                can_see_scope = set()
                for row_x in range(row0, row1):
                    for col_x in range(col0, col1):
                        pos = row_x * 100 + col_x
                        if self.map.is_valid(pos) and self.map.can_see(bop['cur_hex'], pos, mode):
                            dis = self.map.get_distance(bop['cur_hex'], pos)
                            tmp_bop_dis = int(bop_dis / 2) if self.map.is_in_cover(
                                pos) and target_type in [0, 2] else bop_dis  # 如果当前位置是遮蔽地形，则bop可观察距离减少一半
                            if dis <= tmp_bop_dis:
                                can_see_scope.add(pos)
                return can_see_scope
            else:
                # 获得正方形区域
                row, col = bop['cur_hex'] // 100, bop['cur_hex'] % 100
                row0, row1 = min(row - bop_dis, 0), min(row + bop_dis + 1, self.map.max_row)
                col0, col1 = min(col - bop_dis, 0), min(col + bop_dis + 1, self.map.max_col)

                can_see_scope = set()
                for row_x in range(row0, row1):
                    for col_x in range(col0, col1):
                        pos = row_x * 100 + col_x
                        if self.map.is_valid(pos) and self.map.can_see(pos, bop['cur_hex'], mode):
                            dis = self.map.get_distance(bop['cur_hex'], pos)
                            tmp_bop_dis = int(bop_dis / 2) if self.map.is_in_cover(
                                pos) and target_type in [0, 2] else bop_dis  # 如果当前位置是遮蔽地形，则bop可观察距离减少一半
                            if dis <= tmp_bop_dis:
                                can_see_scope.add(pos)
                return can_see_scope
        except Exception as e:
            raise e

    def get_see_scope1(self, bop, target_type=0):
        """
        根据bop对target_type类型的观测距离，计算是否可见相应位置的该类算子
        注： 此处考虑了遮蔽地形，通视距离减半，认为敌方是掩蔽状态的视野信息
            由于新引擎的observe_distance格式由dict类型变为list类型，因此target_type需要输入sub_type
        """
        try:
            bop_dis = bop['observe_distance'][target_type]  # 获得bop对target_bop的通视距离

            if bop["sub_type"] in [5, 7] and target_type == 5:
                mode = 1
                flag = True
            elif bop["sub_type"] in [5, 7] and target_type == 6:
                mode = 5
                flag = True
            elif bop["sub_type"] in [5, 7]:
                mode = 5
                flag = True
            elif bop["sub_type"] in [6, 8] and target_type == 5:
                mode = 5
                flag = False
            elif bop["sub_type"] in [6, 8] and target_type == 6:
                mode = 4
                flag = True
            elif bop["sub_type"] in [6, 8]:
                mode = 3
                flag = True
            elif target_type == 5:
                mode = 2
                flag = False
            elif target_type == 6:
                mode = 3
                flag = False
            else:
                mode = 0
                flag = True

            # 获得正方形区域
            row, col = bop['cur_hex'] // 100, bop['cur_hex'] % 100
            row0, row1 = min(row - bop_dis, 0), min(row + bop_dis + 1, self.map.max_row)
            col0, col1 = min(col - bop_dis, 0), min(col + bop_dis + 1, self.map.max_col)
            if flag:
                can_see_scope = set()
                for row_x in range(row0, row1):
                    for col_x in range(col0, col1):
                        pos = row_x * 100 + col_x
                        if self.map.is_valid(pos) and self.map.can_see(bop['cur_hex'], pos, mode):
                            dis = self.map.get_distance(bop['cur_hex'], pos)
                            tmp_bop_dis0 = int(bop_dis / 2) if self.map.is_in_cover(
                                pos) and target_type in [0, 2] else bop_dis  # 如果当前位置是遮蔽地形，则bop可观察距离减少一半
                            if target_type not in [0, 2]:  # 敌方只有车辆和人员可以掩蔽
                                tmp_bop_dis = tmp_bop_dis0
                            else:
                                flag_hide = False  # True时掩蔽无效
                                if bop["sub_type"] in [5, 7] and target_type == 0:  # 我方是巡飞弹，无人机，车辆掩蔽无效
                                    flag_hide = self.map.get_height_change_level(bop['cur_hex'], pos) \
                                                <= (-1 + 10)
                                    tmp_bop_dis = tmp_bop_dis0 if flag_hide else int(tmp_bop_dis0 / 2)
                                elif bop["sub_type"] in [6, 8] and target_type == 0:
                                    flag_hide = self.map.get_height_change_level(bop['cur_hex'], pos) \
                                                <= (-1 + bop["altitude"] // 20)
                                    tmp_bop_dis = tmp_bop_dis0 if flag_hide else int(tmp_bop_dis0 / 2)
                                else:
                                    tmp_bop_dis = tmp_bop_dis0 if \
                                        self.map.get_height_change_level(bop['cur_hex'], pos) <= -1 \
                                        and target_type == 0 else int(
                                        tmp_bop_dis0 / 2)  # 如果观察点的高程大于算子当前坐标，掩蔽掩蔽状态有效，距离减半
                            if dis <= tmp_bop_dis:
                                can_see_scope.add(pos)
            else:
                can_see_scope = set()
                for row_x in range(row0, row1):
                    for col_x in range(col0, col1):
                        pos = row_x * 100 + col_x
                        if self.map.is_valid(pos) and self.map.can_see(pos, bop['cur_hex'], mode):
                            dis = self.map.get_distance(bop['cur_hex'], pos)
                            tmp_bop_dis0 = int(bop_dis / 2) if self.map.is_in_cover(
                                pos) and target_type in [0, 2] else bop_dis  # 如果当前位置是遮蔽地形，则bop可观察距离减少一半
                            if target_type not in [0, 2]:  # 敌方只有车辆和人员可以掩蔽
                                tmp_bop_dis = tmp_bop_dis0
                            else:
                                flag_hide = False  # True时掩蔽无效
                                if bop["sub_type"] in [5, 7] and target_type == 0:  # 我方是巡飞弹，无人机，车辆掩蔽无效
                                    flag_hide = self.map.get_height_change_level(bop['cur_hex'], pos) \
                                                <= (-1 + 10)
                                    tmp_bop_dis = tmp_bop_dis0 if flag_hide else int(tmp_bop_dis0 / 2)
                                elif bop["sub_type"] in [6, 8] and target_type == 0:
                                    flag_hide = self.map.get_height_change_level(bop['cur_hex'], pos) \
                                                <= (-1 + bop["altitude"] // 20)
                                    tmp_bop_dis = tmp_bop_dis0 if flag_hide else int(tmp_bop_dis0 / 2)
                                else:
                                    tmp_bop_dis = tmp_bop_dis0 if \
                                        self.map.get_height_change_level(bop['cur_hex'], pos) <= -1 \
                                        and target_type == 0 else int(
                                        tmp_bop_dis0 / 2)  # 如果观察点的高程大于算子当前坐标，掩蔽掩蔽状态有效，距离减半
                            if dis <= tmp_bop_dis:
                                can_see_scope.add(pos)
            return can_see_scope
        except Exception as e:
            raise e

    def get_fire_scope(self, bop, target_type=2):
        try:
            can_see_scope = self.get_see_scope(bop)
            fire_scope = set()
            if target_type == 1:
                for can_see in can_see_scope:
                    dis = self.map.get_distance(bop['cur_hex'], can_see)
                    if dis <= 10:
                        fire_scope.add(can_see)
            elif target_type == 2:
                for can_see in can_see_scope:
                    dis = self.map.get_distance(bop['cur_hex'], can_see)
                    if dis <= 20:
                        fire_scope.add(can_see)

            return fire_scope
        except Exception as e:
            raise e

    def gen_send_string_message(self, to_all, msg_body):
        message = {
            "actor": self.seat,
            "type": 204,
            "to_all": to_all,
            "msg_body": msg_body
        }
        return message

    def gen_send_graphic_message(self, msg_body, graphic_type, word=10, color="#ffffff", description="Description"):
        message = {
            "actor": self.seat,
            "type": 205,
            "msg_body": {
                "hexs": msg_body,
                "graphic_type": graphic_type,
                "word": word,
                "color": color,
                "description": description
            }
        }
        return message

    def gen_send_clear_render(self, msg_id):
        message = {
            "actor": self.seat,
            "type": 206,
            "clearing_msg_id": msg_id
        }
        return message

    def execute_mission(self, obj_id, observation, cur_map, mission_type):
        bop = self.get_bop(obj_id)
        operators = self.observation['operators']
        see_enemy_bops = [bop for bop in filter(lambda bop: bop['color'] != self.color, operators)]
        target_pos = None

        if bop['type'] == 2:
            if bop['sub_type'] == 3:
                pos = gen_jm_pos(bop, see_enemy_bops, observation, cur_map)
                if pos:
                    target_pos = [pos]
            else:
                target_pos = target_pos_select(bop, see_enemy_bops, cur_map, mission_type)
        elif bop['type'] == 3:
            target_pos = aircraft_recon_cities(bop, observation, cur_map, see_enemy_bops, mission_type)

        return target_pos
